/*
 * colas_peticiones.c
 *
 *  Created on: 12/10/2011
 *      Author: Normandia
 */

#include "colas_peticiones.h"
#include "threads.h"

REG_COLA_PETICION* generarPeticion(uint8_t tipo_msg, T_MENSAJE* mensaje){

	REG_COLA_PETICION* peticion_generada;

	peticion_generada = malloc(sizeof(REG_COLA_PETICION));

	// Si superó los 64K de petciones, restartea el contador.
	if(contador_peticiones >= 64000)
		contador_peticiones = 0;

	log("RAID", "log.txt", "INFO","Generando una peticion\n");
	peticion_generada->id_peticion = contador_peticiones++;
	peticion_generada->tipo = tipo_msg;
	peticion_generada->mensaje = mensaje;

	return peticion_generada;

}

void eliminarPeticion(REG_COLA_PETICION* peticion){

	eliminarMensaje(peticion->mensaje);
	free(peticion);
}

void nuevoDisco(int socket, uint8_t id_disco) {

	REG_DISCO* disco;

	log("RAID", "log.txt", "INFO","Inicializando los semaforos\n");
	disco = malloc(sizeof(REG_DISCO));
	if(sem_init(&disco->contador_elem, 0, 0) == -1) {
		free(disco);
		return;
	}

	if(sem_init(&disco->pedidos_discos, 0, 1) == -1) {
		free(disco);
		return;
	}
	if(sem_init(&disco->mutex, 0, 1) == -1) {
		free(disco);
		return;
	}
	if(sem_init(&disco->flag, 0, 0) == -1) { //0 lo bloquea
	 	free(disco);
		return;
	}
	if(sem_init(&disco->rep_pedidos_pen, 0, 0) == -1) { //0= repartir
	 	free(disco);
		return;
	}

	disco->cola_pedidos = collection_queue_create();
	disco->id_disco = id_disco;
	disco->socket = socket;

	if(parametros.flag == 1 ){
		printf("Se conecto un disco al RAID. Identificador del disco:%u\n",id_disco);
	}
	asignarThreadADisco(disco);
}


void insertarEnColasRaid(REG_COLA_PETICION* peticion, int sock){

	REG_COLA_RAID* reg_peticiones;

	if(peticion->tipo == TIPO_READSECTOR){
		log("RAID", "log.txt", "INFO","Insertando un pedido de lectura en la COLA de LECTURA del proceso RAID\n");
		collection_queue_push(cola_lectura, (void*) peticion);
	}else{
		log("RAID", "log.txt", "INFO","Insertando un pedido de escritura en la COLA de ESCRITURA del proceso RAID\n");
		collection_queue_push(cola_escritura, (void*) peticion);
	}
	//Genera el registro para la lista de pedidos (ordenados por id)
	log("RAID", "log.txt", "INFO","Insertando una peticion a la lista de pedidos.\n");
	reg_peticiones = malloc(sizeof(REG_COLA_RAID));
	reg_peticiones->sock = sock;
	reg_peticiones->id_peticion = peticion->id_peticion;

	collection_list_add(lista_peticiones_raid,(void*)reg_peticiones);
}

void distribuirPeticiones() {

	REG_COLA_PETICION* peticion;

	log("RAID", "log.txt", "INFO","Realizando la distribucion de pedidos de escritura y lectura.\n");
	// Lecturas
	while(collection_queue_size(cola_lectura) > 0) {
		peticion = (REG_COLA_PETICION*) collection_queue_pop(cola_lectura);
		distribuirLecturaEntreDiscos(peticion);
	}

	// Escrituras
	while(collection_queue_size(cola_escritura) > 0) {
		peticion = (REG_COLA_PETICION*) collection_queue_pop(cola_escritura);
		distribuirEscrituraEntreDiscos(peticion);
	}

}

void distribuirLecturaEntreDiscos(REG_COLA_PETICION* peticion) {

	REG_DISCO *disco_a_agregar = NULL;

	if(disco_actual >= collection_list_size(lista_discos))
		disco_actual = 0;

	if(parametros.flag == 1 ){
		printf("Leyendo del disco:%u\n",disco_actual);
	}

	disco_a_agregar = collection_list_get(lista_discos, disco_actual);
	disco_actual++;

	collection_queue_push(disco_a_agregar->cola_pedidos, (void*) peticion);
	sem_post(&disco_a_agregar->contador_elem);

}

void distribuirEscrituraEntreDiscos(REG_COLA_PETICION* peticion) {

	REG_DISCO *disco_actual;
	REG_COLA_PETICION* peticion_a_encolar;
	uint8_t i;

	for (i = 0; i < collection_list_size(lista_discos);i++) {

		disco_actual = collection_list_get(lista_discos, i);

		peticion_a_encolar = malloc(sizeof(REG_COLA_PETICION));
		peticion_a_encolar->id_peticion = peticion->id_peticion;
		peticion_a_encolar->tipo = peticion->tipo;
		peticion_a_encolar->mensaje = peticion->mensaje;

		collection_queue_push(disco_actual->cola_pedidos, (void*) peticion_a_encolar);

		if(parametros.flag == 1 ){
			printf("Escribiendo en los discos\n");
		}

		sem_post(&disco_actual->contador_elem);
	}
	eliminarPeticion(peticion);
}


int closureColaPeticion(void* data){

	REG_COLA_RAID* nodo;
	nodo = (REG_COLA_RAID*)data;

	return nodo->id_peticion == buffer_id_peticion;

}

int buscaSocketRta(uint64_t id_peticion) {

	REG_COLA_RAID* nodo;
	int socket;

	sem_wait(&mutex_id_peticion);
	buffer_id_peticion = id_peticion;
	nodo = (REG_COLA_RAID*) collection_list_removeByClosure2(lista_peticiones_raid,closureColaPeticion);
	sem_post(&mutex_id_peticion);

	socket = nodo->sock;
	free(nodo);

	return socket;

}


void inicializar() {

	cola_lectura = collection_queue_create();
	cola_escritura = collection_queue_create();

	lista_peticiones_raid = collection_list_create();

	lista_discos = collection_list_create();

	contador_peticiones = 0;
	disco_actual = 0;

	sem_init(&mutex_id_peticion,0,1);
	log("RAID", "log.txt", "INFO","Inicializando las colas y listas\n");
}
